/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pt.iasd.youthshelter.activity;

import java.util.logging.Logger;
import java.awt.Component;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.logging.Level;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JList;
import javax.swing.plaf.basic.BasicComboBoxRenderer;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
import org.netbeans.spi.actions.AbstractSavable;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.awt.UndoRedo;
import org.openide.util.Lookup;
import org.openide.windows.TopComponent;
import org.openide.util.lookup.InstanceContent;
import org.openide.util.lookup.Lookups;
import pt.iasd.youthshelter.datamodel.Activity;
import pt.iasd.youthshelter.datamodel.ActivityType;
import pt.iasd.youthshelter.datamodel.Club;
import pt.iasd.youthshelter.datamodel.service.ActivityController;
import pt.iasd.youthshelter.datamodel.service.ActivityTypeController;
import pt.iasd.youthshelter.datamodel.service.ClubController;

/**
 * Top component which displays something.
 */
//@ConvertAsProperties(
//        dtd = "-//pt.iasd.youthshelter.activity//ActivityEditor//EN",
//        autostore = false)
@TopComponent.Description(
        preferredID = "ActivityEditorTopComponent",
        //iconBase="SET/PATH/TO/ICON/HERE", 
        persistenceType = TopComponent.PERSISTENCE_ALWAYS)
@TopComponent.Registration(mode = "editor", openAtStartup = false)
//@ActionID(category = "Window", id = "pt.iasd.youthshelter.activity.ActivityEditorTopComponent")
//@ActionReference(path = "Menu/File/New" /*, position = 333 */)
//@TopComponent.OpenActionRegistration(
//        displayName = "#CTL_ActivityEditorAction"/*,
//        preferredID = "ActivityEditorTopComponent"*/)
//@Messages({
//    "CTL_ActivityEditorAction=Activity"/*,
//    "CTL_ActivityEditorTopComponent=New activity",
//    "HINT_ActivityEditorTopComponent=Activity editor window."*/
//})
public final class ActivityEditorTopComponent extends TopComponent {
    private static final ResourceBundle bundle = java.util.ResourceBundle.getBundle("pt/iasd/youthshelter/activity/Bundle");
    
    private static final String TAG_BOLD_START = "<b>";
    private static final String TAG_BOLD_END = "</b>";
    private static final String TAG_HTML_START = "<html>";
    private static final String TAG_HTML_END = "</html>";
    
    private UndoRedo.Manager undoRedoManager = new UndoRedo.Manager();
    private InstanceContent instanceContent = new InstanceContent();
    private ActivitySavable savable = new ActivitySavable();
    
    /* Hidden bean creation flag. */
    private boolean create = false;
    
    /* Logging object. */
    private Logger logger = Logger.getLogger(this.getClass().getName());

    public ActivityEditorTopComponent(String activityName) {
        initComponents();
        create = true;
        activity = new Activity();
        activity.setName(activityName);
        this.NameTextField.setText(activityName);
        setName(String.format("%s%s%s%s%s", TAG_HTML_START, TAG_BOLD_START,
                        activityName, TAG_BOLD_END, TAG_HTML_END));
        setToolTipText(bundle.getString("ActivityEditorTopComponent.Hint"));
        putClientProperty(TopComponent.PROP_DRAGGING_DISABLED, Boolean.TRUE);
        putClientProperty(TopComponent.PROP_UNDOCKING_DISABLED, Boolean.TRUE);
        putClientProperty(TopComponent.PROP_MAXIMIZATION_DISABLED, Boolean.TRUE);
    }
    
    public ActivityEditorTopComponent(Activity activity) {
        initComponents();
//        associateLookup(new AbstractLookup(instanceContent));
        create = (activity == null);
        if(create) {
            setName(String.format("%s%s%s%s%s", TAG_HTML_START, TAG_BOLD_START,
                        bundle.getString("ActivityEditorTopComponent.Title"),
                        TAG_BOLD_END, TAG_HTML_END));
        } else {
            setName(activity.getName());
            this.activity = activity;
            TypeComboBox.setSelectedItem(activity.getTypeId());
            KidsBox.setSelected(activity.hasClub(KidsBox.getText()));
            FirebrandBox.setSelected(activity.hasClub(FirebrandBox.getText()));
            PathfinderBox.setSelected(activity.hasClub(PathfinderBox.getText()));
            CompanionBox.setSelected(activity.hasClub(CompanionBox.getText()));
            SeniorBox.setSelected(activity.hasClub(SeniorBox.getText()));
            undoRedoManager.discardAllEdits();
            valuesChanged();
            associateLookup(Lookups.singleton(activity));
        }
        setToolTipText(bundle.getString("ActivityEditorTopComponent.Hint"));
        putClientProperty(TopComponent.PROP_DRAGGING_DISABLED, Boolean.TRUE);
        putClientProperty(TopComponent.PROP_UNDOCKING_DISABLED, Boolean.TRUE);
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {
        bindingGroup = new org.jdesktop.beansbinding.BindingGroup();

        activity = new pt.iasd.youthshelter.datamodel.Activity();
        ClubBoxesGroup = new javax.swing.ButtonGroup();
        NameLabel = new javax.swing.JLabel();
        NameTextField = new javax.swing.JTextField();
        ThemeLabel = new javax.swing.JLabel();
        ThemeTextField = new javax.swing.JTextField();
        PlaceLabel = new javax.swing.JLabel();
        PlaceTextField = new javax.swing.JTextField();
        Separator = new javax.swing.JSeparator();
        TypeComboBox = new javax.swing.JComboBox();
        TypeLabel = new javax.swing.JLabel();
        LevelLabel = new javax.swing.JLabel();
        StartLabel = new javax.swing.JLabel();
        StartDatePicker = new org.jdesktop.swingx.JXDatePicker();
        EndLabel = new javax.swing.JLabel();
        EndDatePicker = new org.jdesktop.swingx.JXDatePicker();
        KidsBox = new javax.swing.JCheckBox();
        FirebrandBox = new javax.swing.JCheckBox();
        PathfinderBox = new javax.swing.JCheckBox();
        CompanionBox = new javax.swing.JCheckBox();
        SeniorBox = new javax.swing.JCheckBox();

        org.openide.awt.Mnemonics.setLocalizedText(NameLabel, bundle.getString("ActivityEditorTopComponent.NameLabel.text"));

        org.jdesktop.beansbinding.Binding binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, activity, org.jdesktop.beansbinding.ELProperty.create("${name}"), NameTextField, org.jdesktop.beansbinding.BeanProperty.create("text"), "NameBinding");
        bindingGroup.addBinding(binding);

        NameTextField.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyReleased(java.awt.event.KeyEvent evt) {
                NameTextFieldKeyReleased(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(ThemeLabel, bundle.getString("ActivityEditorTopComponent.ThemeLabel.text"));

        binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, activity, org.jdesktop.beansbinding.ELProperty.create("${theme}"), ThemeTextField, org.jdesktop.beansbinding.BeanProperty.create("text"), "ThemeBinding");
        bindingGroup.addBinding(binding);

        ThemeTextField.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyReleased(java.awt.event.KeyEvent evt) {
                ThemeTextFieldKeyReleased(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(PlaceLabel, bundle.getString("ActivityEditorTopComponent.PlaceLabel.text"));

        binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, activity, org.jdesktop.beansbinding.ELProperty.create("${place}"), PlaceTextField, org.jdesktop.beansbinding.BeanProperty.create("text"), "PlaceBinding");
        bindingGroup.addBinding(binding);

        PlaceTextField.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyReleased(java.awt.event.KeyEvent evt) {
                PlaceTextFieldKeyReleased(evt);
            }
        });

        Separator.setOrientation(javax.swing.SwingConstants.VERTICAL);

        List<ActivityType> types = loadTypeComboBoxData();
        TypeComboBox.setModel((types == null || types.isEmpty())? new DefaultComboBoxModel() : new DefaultComboBoxModel(types.toArray(new ActivityType[types.size()])));
        TypeComboBox.setCursor(new java.awt.Cursor(java.awt.Cursor.HAND_CURSOR));
        TypeComboBox.setRenderer(new ActivityTypeRenderer());
        TypeComboBox.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                TypeComboBoxActionPerformed(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(TypeLabel, bundle.getString("ActivityEditorTopComponent.TypeLabel.text"));

        org.openide.awt.Mnemonics.setLocalizedText(LevelLabel, bundle.getString("ActivityEditorTopComponent.LevelLabel.text"));

        org.openide.awt.Mnemonics.setLocalizedText(StartLabel, bundle.getString("ActivityEditorTopComponent.StartLabel.text"));

        StartDatePicker.setMaximumSize(new java.awt.Dimension(150, 22));
        StartDatePicker.setMinimumSize(new java.awt.Dimension(150, 22));
        StartDatePicker.setPreferredSize(new java.awt.Dimension(150, 22));

        binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, activity, org.jdesktop.beansbinding.ELProperty.create("${startDate}"), StartDatePicker, org.jdesktop.beansbinding.BeanProperty.create("date"), "StartDateBinding");
        binding.setSourceNullValue(new Date());
        bindingGroup.addBinding(binding);

        StartDatePicker.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                StartDatePickerActionPerformed(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(EndLabel, bundle.getString("ActivityEditorTopComponent.EndLabel.text"));

        EndDatePicker.setMaximumSize(new java.awt.Dimension(150, 22));
        EndDatePicker.setMinimumSize(new java.awt.Dimension(150, 22));
        EndDatePicker.setPreferredSize(new java.awt.Dimension(150, 22));

        binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, activity, org.jdesktop.beansbinding.ELProperty.create("${endDate}"), EndDatePicker, org.jdesktop.beansbinding.BeanProperty.create("date"), "EndDateBinding");
        binding.setSourceNullValue(new Date());
        bindingGroup.addBinding(binding);

        EndDatePicker.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                EndDatePickerActionPerformed(evt);
            }
        });

        ClubBoxesGroup.add(KidsBox);
        org.openide.awt.Mnemonics.setLocalizedText(KidsBox, bundle.getString("ActivityEditorTopComponent.KidsBox.text"));

        ClubBoxesGroup.add(FirebrandBox);
        org.openide.awt.Mnemonics.setLocalizedText(FirebrandBox, bundle.getString("ActivityEditorTopComponent.FirebrandBox.text"));

        ClubBoxesGroup.add(PathfinderBox);
        org.openide.awt.Mnemonics.setLocalizedText(PathfinderBox, bundle.getString("ActivityEditorTopComponent.PathfinderBox.text"));

        ClubBoxesGroup.add(CompanionBox);
        org.openide.awt.Mnemonics.setLocalizedText(CompanionBox, bundle.getString("ActivityEditorTopComponent.CompanionBox.text"));

        ClubBoxesGroup.add(SeniorBox);
        org.openide.awt.Mnemonics.setLocalizedText(SeniorBox, bundle.getString("ActivityEditorTopComponent.SeniorBox.text"));

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(ThemeLabel)
                    .addComponent(NameLabel)
                    .addComponent(PlaceLabel))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addGap(21, 21, 21)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addGroup(layout.createSequentialGroup()
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                                    .addComponent(TypeLabel)
                                    .addComponent(LevelLabel))
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(TypeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 150, javax.swing.GroupLayout.PREFERRED_SIZE))
                            .addGroup(layout.createSequentialGroup()
                                .addGap(10, 10, 10)
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                    .addComponent(FirebrandBox)
                                    .addComponent(KidsBox)
                                    .addComponent(PathfinderBox)
                                    .addComponent(CompanionBox)
                                    .addComponent(SeniorBox))))
                        .addGap(51, 51, 51)
                        .addComponent(Separator, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(39, 39, 39)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                            .addComponent(StartLabel)
                            .addComponent(EndLabel))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                            .addComponent(EndDatePicker, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(StartDatePicker, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                        .addGap(0, 0, Short.MAX_VALUE))
                    .addComponent(NameTextField)
                    .addComponent(ThemeTextField)
                    .addComponent(PlaceTextField))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(NameLabel)
                    .addComponent(NameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(ThemeLabel)
                    .addComponent(ThemeTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(PlaceTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(PlaceLabel))
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addGroup(layout.createSequentialGroup()
                                .addGap(37, 37, 37)
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                    .addComponent(TypeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addComponent(TypeLabel))
                                .addGap(18, 18, 18)
                                .addComponent(LevelLabel)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(KidsBox)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(FirebrandBox))
                            .addGroup(layout.createSequentialGroup()
                                .addGap(35, 35, 35)
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                    .addComponent(StartLabel)
                                    .addComponent(StartDatePicker, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                                .addGap(18, 18, 18)
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                    .addComponent(EndDatePicker, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addComponent(EndLabel))))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(PathfinderBox)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(CompanionBox)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(SeniorBox))
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(Separator, javax.swing.GroupLayout.PREFERRED_SIZE, 188, javax.swing.GroupLayout.PREFERRED_SIZE)))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        NameTextField.getDocument().addUndoableEditListener(undoRedoManager);
        ThemeTextField.getDocument().addUndoableEditListener(undoRedoManager);
        PlaceTextField.getDocument().addUndoableEditListener(undoRedoManager);

        bindingGroup.bind();
    }// </editor-fold>//GEN-END:initComponents

    private void NameTextFieldKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_NameTextFieldKeyReleased
//        if(activity == null) {
//            valuesChanged();
//        } else if(!activity.getName().equals(NameTextField.getText())) {
            valuesChanged();
//        }
    }//GEN-LAST:event_NameTextFieldKeyReleased

    private void ThemeTextFieldKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_ThemeTextFieldKeyReleased
//        if(activity == null) {
//            valuesChanged();
//        } else if(!activity.getTheme().equals(ThemeTextField.getText())) {
            valuesChanged();
//        }
    }//GEN-LAST:event_ThemeTextFieldKeyReleased

    private void PlaceTextFieldKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_PlaceTextFieldKeyReleased
//        if(activity == null) {
//            valuesChanged();
//        } else if(!activity.getPlace().equals(PlaceTextField.getText())) {
            valuesChanged();
//        }
    }//GEN-LAST:event_PlaceTextFieldKeyReleased

    private void TypeComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_TypeComboBoxActionPerformed
        if(activity == null) {
            valuesChanged();
        } else {
            int typeId = ((ActivityType)TypeComboBox.getSelectedItem()).getId();
            if(activity.getTypeId().getId() != typeId) {
                valuesChanged();
            }
        }
    }//GEN-LAST:event_TypeComboBoxActionPerformed

    private void StartDatePickerActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_StartDatePickerActionPerformed
//        if(activity == null) {
//            valuesChanged();
//        } else if(activity.getStartDate().compareTo(StartDatePicker.getDate()) != 0) {
            valuesChanged();
//        }
    }//GEN-LAST:event_StartDatePickerActionPerformed

    private void EndDatePickerActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_EndDatePickerActionPerformed
//        if(activity == null) {
//            valuesChanged();
//        } else if(activity.getEndDate().compareTo(EndDatePicker.getDate()) != 0) {
            valuesChanged();
//        }
    }//GEN-LAST:event_EndDatePickerActionPerformed

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.ButtonGroup ClubBoxesGroup;
    private javax.swing.JCheckBox CompanionBox;
    private org.jdesktop.swingx.JXDatePicker EndDatePicker;
    private javax.swing.JLabel EndLabel;
    private javax.swing.JCheckBox FirebrandBox;
    private javax.swing.JCheckBox KidsBox;
    private javax.swing.JLabel LevelLabel;
    private javax.swing.JLabel NameLabel;
    private javax.swing.JTextField NameTextField;
    private javax.swing.JCheckBox PathfinderBox;
    private javax.swing.JLabel PlaceLabel;
    private javax.swing.JTextField PlaceTextField;
    private javax.swing.JCheckBox SeniorBox;
    private javax.swing.JSeparator Separator;
    private org.jdesktop.swingx.JXDatePicker StartDatePicker;
    private javax.swing.JLabel StartLabel;
    private javax.swing.JLabel ThemeLabel;
    private javax.swing.JTextField ThemeTextField;
    private javax.swing.JComboBox TypeComboBox;
    private javax.swing.JLabel TypeLabel;
    private pt.iasd.youthshelter.datamodel.Activity activity;
    private org.jdesktop.beansbinding.BindingGroup bindingGroup;
    // End of variables declaration//GEN-END:variables
    
    @Override
    public boolean canClose() {
        Collection<? extends ActivitySavable> savables = getLookup().lookupAll(ActivitySavable.class);
        if(savables != null && savables.contains(savable)) {
            NotifyDescriptor.Confirmation confirm = new NotifyDescriptor.Confirmation(
                    bundle.getString("Confirmation.Editor.Message")
                        .replace("\n", System.getProperty("line.separator")),
                    bundle.getString("Confirmation.Editor.Title")
            );
            Object option = DialogDisplayer.getDefault().notify(confirm);
            if(option == NotifyDescriptor.YES_OPTION) {
                getLookup().lookup(ActivitySavable.class).handleSave();
                return true;
            } else if(option == NotifyDescriptor.NO_OPTION) {
                savable.cancelSave();
                return true;
            } else {
                return false;
            }
        } else {
            return true;
        }
    }

    private void writeProperties(Properties p) {
        // better to version settings since initial version as advocated at
        // http://wiki.apidesign.org/wiki/PropertyFiles
        p.setProperty("version", "1.0");
        // store your settings
    }

    private void readProperties(Properties p) {
        String version = p.getProperty("version");
        // read your settings according to their version
    }
    
    private void valuesChanged() {
        if(undoRedoManager.canUndo()) {
            if(getLookup().lookup(ActivitySavable.class) == null) {
                instanceContent.remove(savable);
                instanceContent.add(savable);
            }
            setName(String.format("%s%s%s%s%s", TAG_HTML_START, TAG_BOLD_START,
                    (NameTextField.getText().isEmpty())? getName() : NameTextField.getText(),
                    TAG_BOLD_END, TAG_HTML_END));
        } else {
            instanceContent.remove(savable);
            setName(((NameTextField.getText().isEmpty())? getDefaultName() : NameTextField.getText()));
        }
    }

    @Override
    public UndoRedo getUndoRedo() {
        return undoRedoManager;
    }
    
    private String getDefaultName() {
        return bundle.getString("ActivityEditorTopComponent.Title");
    }
    
    // <editor-fold defaultstate="collapsed" desc="Inner Classes">
    private class ActivitySavable extends AbstractSavable {
        public ActivitySavable() {
            register();
        }
        
        public void cancelSave() {
            instanceContent.remove(this);
            unregister();
        }
        
        @Override
        protected String findDisplayName() {
            return getName();
        }

        @Override
        protected void handleSave() {
            /* Check not null requirements */
            if(NameTextField.getText() == null || NameTextField.getText().isEmpty()) {
                NotifyDescriptor.Message alert = new NotifyDescriptor.Message(
                        bundle.getString("NoActivityName.Error")
                                .replace("\n", System.getProperty("line.separator")),
                        NotifyDescriptor.ERROR_MESSAGE);
                DialogDisplayer.getDefault().notify(alert);
                return;
            }
            
            /* Create progress bar handle */
            ProgressHandle pHandle = ProgressHandleFactory.createHandle(
                    bundle.getString("ProgressBarSave"));
            pHandle.start(3);
            
            /* Set activity parameters. */
            activity.setName(NameTextField.getText());
            String theme = ThemeTextField.getText();
            if(theme != null && !theme.isEmpty()) {
                activity.setTheme(theme);
            }
            String place = PlaceTextField.getText();
            if(place != null && !place.isEmpty()) {
                activity.setPlace(place);
            }
            activity.setTypeId((ActivityType)TypeComboBox.getSelectedItem());
            ClubController clubController = Lookup.getDefault().lookup(ClubController.class);
            List<Club> clubList = new ArrayList<>(), clubs = clubController.findClubEntities();
            if(KidsBox.isSelected()) {
                for(Club club : clubs) {
                    if(club.getName().equalsIgnoreCase(KidsBox.getText())) {
                        clubList.add(club);
                        clubs.remove(club);
                        break;
                    }
                }
            }
            if(FirebrandBox.isSelected()) {
                for(Club club : clubs) {
                    if(club.getName().equalsIgnoreCase(FirebrandBox.getText())) {
                        clubList.add(club);
                        clubs.remove(club);
                        break;
                    }
                }
            }
            if(PathfinderBox.isSelected()) {
                for(Club club : clubs) {
                    if(club.getName().equalsIgnoreCase(PathfinderBox.getText())) {
                        clubList.add(club);
                        clubs.remove(club);
                        break;
                    }
                }
            }
            if(CompanionBox.isSelected()) {
                for(Club club : clubs) {
                    if(club.getName().equalsIgnoreCase(CompanionBox.getText())) {
                        clubList.add(club);
                        clubs.remove(club);
                        break;
                    }
                }
            }
            if(SeniorBox.isSelected()) {
                for(Club club : clubs) {
                    if(club.getName().equalsIgnoreCase(SeniorBox.getText())) {
                        clubList.add(club);
                        break;
                    }
                }
            }
            activity.setClubList(clubList);
            activity.setStartDate(StartDatePicker.getDate());
            activity.setEndDate(EndDatePicker.getDate());
            pHandle.progress(1);
            
            /* Persist activity in database. */
            ActivityController controller = Lookup.getDefault().lookup(ActivityController.class);
            if(controller != null) {
                try{
                    if(create) {
                        controller.create(activity);
                    } else {
                        controller.edit(activity);
                    }
                } catch(Exception ex) {
                    logger.log(Level.WARNING, ex.getLocalizedMessage(), ex);
                }
            }
            pHandle.progress(1);
            
            /* Remove this savable from lookup. */
            instanceContent.remove(this);
            setName(activity.getName());
            unregister();
            pHandle.progress(1);
            pHandle.finish();
        }

        @Override
        public boolean equals(Object o) {
            if(o instanceof ActivitySavable) {
                return this == (ActivitySavable) o;
            }
            return false;
        }

        @Override
        public int hashCode() {
            return ActivityEditorTopComponent.this.hashCode();
        }
    }
    
    private class ActivityTypeRenderer extends BasicComboBoxRenderer {
        @Override
        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
            super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
            if(value instanceof ActivityType) {
                setText(((ActivityType)value).getName());
            } else {
                setText("");
            }
            return this;
        }
    }// </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="ComboBox Data Loading Code">
    private List<ActivityType> loadTypeComboBoxData() {
        ActivityTypeController controller = Lookup.getDefault().lookup(ActivityTypeController.class);
        if(controller != null) {
            try{
                return controller.findActivityTypeEntities();
            } catch(Exception ex) {
                logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
            }
        }
        return null;
    }// </editor-fold>
}
